package br.com.cs.controller;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.transaction.UserTransaction;

public class AbstractController implements Serializable {

    private static final long serialVersionUID = 1L;
    @PersistenceUnit
    private EntityManagerFactory emf;
    @Resource
    private UserTransaction userTransaction;

    protected final <T> T doInTransaction(PersistenceAction<T> action) throws ControllerException {
        EntityManager em = emf.createEntityManager();
        try {
            userTransaction.begin();
            T result = action.execute(em);
            userTransaction.commit();
            return result;
        } catch (Exception e) {
            try {
                userTransaction.rollback();
            } catch (Exception ex) {
                Logger.getLogger(AbstractController.class.getName()).log(Level.SEVERE, null, ex);
            }
            throw new ControllerException(e);
        } finally {
            em.close();
        }

    }

    protected final void doInTransaction(PersistenceActionWithoutResult action) throws ControllerException {
        EntityManager em = emf.createEntityManager();
        try {
            userTransaction.begin();
            action.execute(em);
            userTransaction.commit();
        } catch (Exception e) {
            try {
                userTransaction.rollback();
            } catch (Exception ex) {
                Logger.getLogger(AbstractController.class.getName()).log(Level.SEVERE, null, ex);
            }
            throw new ControllerException(e);
        } finally {
            em.close();
        }
    }

    protected static interface PersistenceAction<T> {

        T execute(EntityManager em);
    }

    protected static interface PersistenceActionWithoutResult {

        void execute(EntityManager em);
    }

    protected final <E> List<E> executeNamedQuery(final String namedQuery,
            final Class<E> resultType, final Map<String, Object> parameters)
            throws ControllerException {
        try {
            return doInTransaction(new PersistenceAction<List<E>>() {

                @Override
                public List<E> execute(EntityManager em) {
                    // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=300412
                    // TypedQuery<E> query = em.createNamedQuery(namedQuery, resultType);
                    Query query = em.createNamedQuery(namedQuery);
                    if (parameters != null && !parameters.isEmpty()) {
                        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                            query.setParameter(entry.getKey(), entry.getValue());
                        }
                    }
                    // return query.getResultList();
                    return (List<E>) query.getResultList();
                }
            });
        } catch (Exception e) {
            throw new ControllerException("Erro ao executar 'named query' ("
                    + namedQuery + ") com parametros (" + parameters + ").", e);
        }
    }

    protected final <E> List<E> executeNamedQuery(String namedQuery, Class<E> resultType) {
        return this.executeNamedQuery(namedQuery, resultType, (Map<String, Object>) null);
    }

    protected final <E> List<E> executeNamedQuery(final String namedQuery,
            final Class<E> resultType, final Object... parameters)
            throws ControllerException {
        try {
            return doInTransaction(new PersistenceAction<List<E>>() {

                @Override
                public List<E> execute(EntityManager em) {
                    // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=300412
                    // TypedQuery<E> query = em.createNamedQuery(namedQuery, resultType);
                    Query query = em.createNamedQuery(namedQuery);
                    if (parameters != null && parameters.length > 0) {
                        for (int i = 0; i < parameters.length; i++) {
                            query.setParameter(i + 1, parameters[i]);
                        }
                    }
                    // return query.getResultList();
                    return (List<E>) query.getResultList();
                }
            });
        } catch (Exception e) {
            throw new ControllerException("Erro ao executar 'named query' ("
                    + namedQuery + ") com parametros (" + parameters + ").", e);
        }
    }

    protected void addMessage(String componentId, Severity severity, String summary, String details) {
        FacesMessage facesMessage = new FacesMessage(severity, summary, details);
        FacesContext.getCurrentInstance().addMessage(componentId,facesMessage);
    }
    
    protected void addInfo(String componentId, String summary, String details) {
        addMessage(componentId, FacesMessage.SEVERITY_INFO, summary, details);
    }
    
    protected void addError(String componentId, String summary, String details) {
        addMessage(componentId, FacesMessage.SEVERITY_ERROR, summary, details);
    }
    
    protected void addFatal(String componentId, String summary, String details) {
        addMessage(componentId, FacesMessage.SEVERITY_FATAL, summary, details);
    }
    
    protected void addWarn(String componentId, String summary, String details) {
        addMessage(componentId, FacesMessage.SEVERITY_WARN, summary, details);
    }
    
}

